Search 算法20题代码解释
好的,我们来用中文详细解释一下第20题的解法。
问题分析
第20题要求我们为一个在网格中行驶的汽车找到一条最优路径。和之前的问题不同,这道题的状态不仅包括汽车的位置(行和列),还包括它的朝向(上、下、左、右)。此外,汽车的移动成本也变得更复杂:直行、左转和右转的成本是不同的。
核心思想:动态规划 (Value Iteration)
解决这类问题的经典方法是动态规划,具体来说是价值迭代 (Value Iteration) 算法。这个思想和第18题的 compute_value
函数非常相似,但我们需要将它扩展到一个三维的状态空间。
状态空间 (State Space):
- 之前我们的状态只需要
(row, col)
。 - 现在,因为朝向会影响下一步的移动和成本,所以我们的状态必须是
(row, col, direction)
。我们有4个朝向,所以状态空间的大小是行数 x 列数 x 4
。
- 之前我们的状态只需要
价值函数 (Value Function):
- 我们需要一个三维的
value
表,value[d][r][c]
记录的是:当汽车在(r, c)
位置并且朝向为d
时,到达终点所需的最小成本。 - 我们将这个表初始化为一个非常大的数(例如999),表示初始时我们不知道到达终点的成本。
- 我们需要一个三维的
策略函数 (Policy Function):
- 同样,我们需要一个三维的
policy
表,policy[d][r][c]
记录的是:当汽车在(r, c)
位置并且朝向为d
时,为了以最小成本到达终点,它下一步应该执行什么动作(右转’R’,直行’#’,或左转’L’)。
- 同样,我们需要一个三维的
算法步骤
算法分为两个主要阶段:
第一阶段:计算所有状态的价值 (Value Iteration)
这个阶段的目标是填满 value
和 policy
这两个三维表。我们使用一个循环,不断地更新每个状态的价值,直到价值不再变化为止。这个过程就像水波从终点反向传播一样。
初始化:
- 将所有状态的
value
设为 999。 - 将终点
goal
的value
设为 0,因为在终点时,到达终点的成本就是0。它的policy
设为*
。
- 将所有状态的
迭代更新:
- 我们用一个
while change
循环,只要在上一轮迭代中有任何value
值被更新,就继续循环。 - 在循环内部,我们遍历每一个可能的状态
(r, c, d)
。 对于一个非终点的状态
(r, c, d)
,我们尝试三种可能的动作(右转、直行、左转):- 对于每一种动作,我们计算出执行该动作后的新朝向
d2
和新位置(r2, c2)
。 - 然后,我们计算通过这个动作到达终点的总成本:
v2 = 动作成本 + value[d2][r2][c2]
。这个公式的含义是:从(r, c, d)
出发,执行这个动作,然后从新的状态(r2, c2, d2)
出发到达终点的最小成本。 - 我们比较这个
v2
和当前记录的value[d][r][c]
。如果v2
更小,说明我们找到了一个更好的路径。我们就更新value[d][r][c] = v2
,并把对应的动作记录到policy[d][r][c]
中。
- 对于每一种动作,我们计算出执行该动作后的新朝向
这个过程会一直持续,直到网络中所有节点的
value
都收敛到最优值。
- 我们用一个
第二阶段:回溯路径 (Traceback)
当我们完成了第一阶段,policy
表里就存储了从任何状态出发的最佳决策。现在,我们只需要从给定的 init
初始状态 (r, c, d)
开始,按照 policy
表的指引一步一步走,就可以得到最优路径。
- 从初始状态开始:获取
init
的r, c, d
。 - 循环移动:
- 在
policy2D
这个二维网格的(r, c)
位置,记录下当前状态(r, c, d)
对应的最佳动作policy[d][r][c]
。 - 根据这个动作,计算出汽车的新朝向和新位置。
- 更新
r, c, d
为新的值。 - 重复这个过程,直到汽车到达
goal
位置。
- 在
- 标记终点:最后,在
policy2D
的终点位置标记一个*
。
最终返回的 policy2D
就是我们要求解的、从特定初始状态出发的最优路径图。
代码解析
python
1 | def optimum_policy2D(grid,init,goal,cost): |